/* ==========================================================================
* Synopsys DesignWare Sensor and Control IP Subsystem IO Software Driver and
* documentation (hereinafter, "Software") is an Unsupported proprietary work
* of Synopsys, Inc. unless otherwise expressly agreed to in writing between
* Synopsys and you.
*
* The Software IS NOT an item of Licensed Software or Licensed Product under
* any End User Software License Agreement or Agreement for Licensed Product
* with Synopsys or any supplement thereto. You are permitted to use and
* redistribute this Software in source and binary forms, with or without
* modification, provided that redistributions of source code must retain this
* notice. You may not view, use, disclose, copy or distribute this file or
* any information contained herein except pursuant to this license grant from
* Synopsys. If you do not agree with this notice, including the disclaimer
* below, then you are not authorized to use the Software.
*
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
* ========================================================================== */
/*==========================================================================
* Library DW_DFSS-1.1.6
* ========================================================================== */

#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include "gpio.h"
#include "io_config.h"
#include "apexextensions.h"


/* EIA GPIO device registers  */
#define SWPORTA_DR      (0x00)  /* GPIO Port A Data Register */
#define SWPORTA_DDR     (0x04)  /* GPIO Port A Data Direction Register */
#define INTEN           (0x30)  /* GPIO Interrupt Enable Register */
#define INTMASK         (0x34)  /* GPIO Interrupt Mask Register */
#define INTTYPE_LEVEL   (0x38)  /* GPIO Interrupt Type Register */
#define INT_POLARITY    (0x3c)  /* GPIO Interrupt Polarity Register */
#define INTSTATUS       (0x44)  /* GPIO Interrupt Status Register */
#define DEBOUNCE        (0x48)  /* GPIO Debounce Enable Register */
#define PORTA_EOI       (0x4c)  /* GPIO Port A Clear Interrupt Register */
#define EXT_PORTA       (0x50)  /* GPIO External Port A Register */
#define LS_SYNC         (0x60)  /* GPIO Level-Sensitive Sync Enable Register */
#define INT_BOTHEDGE    (0x68)  /* GPIO Both Edge Register */
#define CLKEN           (0x80)  /* GPIO Clock Enable Register */

typedef _Interrupt void (*ISR) ();

/* Private data structure maintained by the driver. */
typedef struct gpio_info_struct {
    uint32_t reg_base;      /* base address of device register set */
    /* Callbacks */
    IO_CB_FUNC rx_cb;
    uint8_t opened;
    uint8_t instID;
    /* Interrupt numbers and handlers */
    uint8_t vector;     /* GPIO ISR vector */
    ISR isr;            /* GPIO ISR */
    /* Config option */
    uint8_t enableDebounce;
} gpio_info_t, *gpio_info_pt;


#ifdef IO_GPIO0_PRESENT
#define GPIO_DEV_PRESENT
#endif
#ifdef IO_GPIO1_PRESENT
#define GPIO_DEV_PRESENT
#endif
#ifdef IO_GPIO2_PRESENT
#define GPIO_DEV_PRESENT
#endif
#ifdef IO_GPIO3_PRESENT
#define GPIO_DEV_PRESENT
#endif
#ifdef IO_GPIO_8B0_PRESENT
#define GPIO_DEV_PRESENT
#endif
#ifdef IO_GPIO_8B1_PRESENT
#define GPIO_DEV_PRESENT
#endif
#ifdef IO_GPIO_8B2_PRESENT
#define GPIO_DEV_PRESENT
#endif
#ifdef IO_GPIO_8B3_PRESENT
#define GPIO_DEV_PRESENT
#endif
#ifdef IO_GPIO_4B0_PRESENT
#define GPIO_DEV_PRESENT
#endif
#ifdef IO_GPIO_4B1_PRESENT
#define GPIO_DEV_PRESENT
#endif
#ifdef IO_GPIO_4B2_PRESENT
#define GPIO_DEV_PRESENT
#endif
#ifdef IO_GPIO_4B3_PRESENT
#define GPIO_DEV_PRESENT
#endif

#ifdef GPIO_DEV_PRESENT

#define GPIO_32_MAX_CNT (4)
#define GPIO_8B_MAX_CNT (4)
#define GPIO_4B_MAX_CNT (4)
#define GPIO_MAX_CNT    ((GPIO_32_MAX_CNT) + (GPIO_8B_MAX_CNT) + (GPIO_4B_MAX_CNT))

static void gpio_ISR_proc(uint32_t dev_id);

#ifdef IO_GPIO0_PRESENT
static _Interrupt void gpio0_ISR()
{
    gpio_ISR_proc(0);
}
#endif
#ifdef IO_GPIO1_PRESENT
static _Interrupt void gpio1_ISR()
{
    gpio_ISR_proc(1);
}
#endif
#ifdef IO_GPIO2_PRESENT
static _Interrupt void gpio2_ISR()
{
    gpio_ISR_proc(2);
}
#endif
#ifdef IO_GPIO3_PRESENT
static _Interrupt void gpio3_ISR()
{
    gpio_ISR_proc(3);
}
#endif

#ifdef IO_GPIO_8B0_PRESENT
static _Interrupt void gpio_8B_0_ISR()
{
    gpio_ISR_proc(0 + GPIO_32_MAX_CNT);
}
#endif
#ifdef IO_GPIO_8B1_PRESENT
static _Interrupt void gpio_8B_1_ISR()
{
    gpio_ISR_proc(1 + GPIO_32_MAX_CNT);
}
#endif
#ifdef IO_GPIO_8B2_PRESENT
static _Interrupt void gpio_8B_2_ISR()
{
    gpio_ISR_proc(2 + GPIO_32_MAX_CNT);
}
#endif
#ifdef IO_GPIO_8B3_PRESENT
static _Interrupt void gpio_8B_3_ISR()
{
    gpio_ISR_proc(3 + GPIO_32_MAX_CNT);
}
#endif

#ifdef IO_GPIO_4B0_PRESENT
static _Interrupt void gpio_4B_0_ISR()
{
    gpio_ISR_proc(0 + GPIO_32_MAX_CNT + GPIO_8B_MAX_CNT);
}
#endif
#ifdef IO_GPIO_4B1_PRESENT
static _Interrupt void gpio_4B_1_ISR()
{
    gpio_ISR_proc(1 + GPIO_32_MAX_CNT + GPIO_8B_MAX_CNT);
}
#endif
#ifdef IO_GPIO_4B2_PRESENT
static _Interrupt void gpio_4B_2_ISR()
{
    gpio_ISR_proc(2 + GPIO_32_MAX_CNT + GPIO_8B_MAX_CNT);
}
#endif
#ifdef IO_GPIO_4B3_PRESENT
static _Interrupt void gpio_4B_3_ISR()
{
    gpio_ISR_proc(3 + GPIO_32_MAX_CNT + GPIO_8B_MAX_CNT);
}
#endif

/* GPIO devices private data structures */
static gpio_info_pt gpio_handles[GPIO_MAX_CNT] = { 0 };

static gpio_info_t gpio_devs[] = {
#ifdef IO_GPIO0_PRESENT
    {.instID = IO_GPIO_DEV_ID(0, 32),
     .reg_base = AR_IO_GPIO0_SWPORTA_DR,
     .vector = IO_GPIO0_INT_INTR_FLAG,
     .isr = gpio0_ISR,
     .enableDebounce = IO_GPIO0_DEBOUNCE},
#endif
#ifdef IO_GPIO1_PRESENT
    {.instID = IO_GPIO_DEV_ID(1, 32),
     .reg_base = AR_IO_GPIO1_SWPORTA_DR,
     .vector = IO_GPIO1_INT_INTR_FLAG,
     .isr = gpio1_ISR,
     .enableDebounce = IO_GPIO1_DEBOUNCE},
#endif
#ifdef IO_GPIO2_PRESENT
    {.instID = IO_GPIO_DEV_ID(2, 32),
     .reg_base = AR_IO_GPIO2_SWPORTA_DR,
     .vector = IO_GPIO2_INT_INTR_FLAG,
     .isr = gpio2_ISR,
     .enableDebounce = IO_GPIO2_DEBOUNCE},
#endif
#ifdef IO_GPIO3_PRESENT
    {.instID = IO_GPIO_DEV_ID(3, 32),
     .reg_base = AR_IO_GPIO3_SWPORTA_DR,
     .vector = IO_GPIO3_INT_INTR_FLAG,
     .isr = gpio3_ISR,
     .enableDebounce = IO_GPIO3_DEBOUNCE},
#endif

#ifdef IO_GPIO_8B0_PRESENT
    {.instID = IO_GPIO_DEV_ID(0, 8),
     .reg_base = AR_IO_GPIO_8B0_SWPORTA_DR,
     .vector = IO_GPIO_8B0_INT_INTR_FLAG,
     .isr = gpio_8B_0_ISR,
     .enableDebounce = IO_GPIO_8B0_DEBOUNCE},
#endif
#ifdef IO_GPIO_8B1_PRESENT
    {.instID = IO_GPIO_DEV_ID(1, 8),
     .reg_base = AR_IO_GPIO_8B1_SWPORTA_DR,
     .vector = IO_GPIO_8B1_INT_INTR_FLAG,
     .isr = gpio_8B_1_ISR,
     .enableDebounce = IO_GPIO_8B1_DEBOUNCE},
#endif
#ifdef IO_GPIO_8B2_PRESENT
    {.instID = IO_GPIO_DEV_ID(2, 8),
     .reg_base = AR_IO_GPIO_8B2_SWPORTA_DR,
     .vector = IO_GPIO_8B2_INT_INTR_FLAG,
     .isr = gpio_8B_2_ISR,
     .enableDebounce = IO_GPIO_8B2_DEBOUNCE},
#endif
#ifdef IO_GPIO_8B3_PRESENT
    {.instID = IO_GPIO_DEV_ID(3, 8),
     .reg_base = AR_IO_GPIO_8B3_SWPORTA_DR,
     .vector = IO_GPIO_8B3_INT_INTR_FLAG,
     .isr = gpio_8B_3_ISR,
     .enableDebounce = IO_GPIO_8B3_DEBOUNCE},
#endif

#ifdef IO_GPIO_4B0_PRESENT
    {.instID = IO_GPIO_DEV_ID(0, 4),
     .reg_base = AR_IO_GPIO_4B0_SWPORTA_DR,
     .vector = IO_GPIO_4B0_INT_INTR_FLAG,
     .isr = gpio_4B_0_ISR,
     .enableDebounce = IO_GPIO_4B0_DEBOUNCE},
#endif
#ifdef IO_GPIO_4B1_PRESENT
    {.instID = IO_GPIO_DEV_ID(1, 4),
     .reg_base = AR_IO_GPIO_4B1_SWPORTA_DR,
     .vector = IO_GPIO_4B1_INT_INTR_FLAG,
     .isr = gpio_4B_1_ISR,
     .enableDebounce = IO_GPIO_4B1_DEBOUNCE},
#endif
#ifdef IO_GPIO_4B2_PRESENT
    {.instID = IO_GPIO_DEV_ID(2, 4),
     .reg_base = AR_IO_GPIO_4B2_SWPORTA_DR,
     .vector = IO_GPIO_4B2_INT_INTR_FLAG,
     .isr = gpio_4B_2_ISR,
     .enableDebounce = IO_GPIO_4B2_DEBOUNCE},
#endif
#ifdef IO_GPIO_4B3_PRESENT
    {.instID = IO_GPIO_DEV_ID(3, 4),
     .reg_base = AR_IO_GPIO_4B3_SWPORTA_DR,
     .vector = IO_GPIO_4B3_INT_INTR_FLAG,
     .isr = gpio_4B_3_ISR,
     .enableDebounce = IO_GPIO_4B3_DEBOUNCE},
#endif

    {.instID = GPIO_MAX_CNT}
};



#define REG_WRITE( reg, x )   _sr( (unsigned)(x), (unsigned)(dev->reg_base + reg) )
#define REG_READ( reg )       _lr( (unsigned)(dev->reg_base + reg) )


uint32_t io_gpio_open(uint32_t dev_id)
{
    uint32_t h = 0;
    gpio_info_pt dev;

    h = 0;
    while ((gpio_devs[h].instID != dev_id)
       && (gpio_devs[h].instID != GPIO_MAX_CNT)) {
    h++;
    }
    if ((gpio_devs[h].instID == GPIO_MAX_CNT) || (0 != gpio_handles[dev_id])) { /* dev_id not part of design, or still open */
    return 1;
    }
    gpio_handles[dev_id] = &gpio_devs[h];
    dev = gpio_handles[dev_id];

    dev->opened = 1;
    /* enable the clock and enable synchronization */
    REG_WRITE(CLKEN, 0x1);
    REG_WRITE(LS_SYNC, 0x1);
    _setvecti(dev->vector, dev->isr);

    return 0;
}


void io_gpio_close(uint32_t dev_id)
{
    gpio_info_pt dev = gpio_handles[dev_id];

    REG_WRITE(INTEN, 0x0);
    REG_WRITE(LS_SYNC, 0x0);
    REG_WRITE(CLKEN, 0x0);
    _setvecti(dev->vector, NULL);
    dev->rx_cb = NULL;
    dev->opened = 0;
    gpio_handles[dev_id] = 0;
}


void io_gpio_read(uint32_t dev_id, uint32_t * data)
{
    gpio_info_pt dev = gpio_handles[dev_id];

    *data = REG_READ(EXT_PORTA);
}


void io_gpio_write(uint32_t dev_id, uint32_t data)
{
    gpio_info_pt dev = gpio_handles[dev_id];

    REG_WRITE(SWPORTA_DR, data);
}


void io_gpio_ioctl(uint32_t dev_id, uint32_t cmd, void *arg)
{
    gpio_info_pt dev = gpio_handles[dev_id];

    switch (cmd) {
    case IO_SET_CB_RX:
    dev->rx_cb = ((io_cb_t *) arg)->cb;
    break;

    case IO_GPIO_SET_DIRECTION:
    REG_WRITE(SWPORTA_DDR, *((uint32_t *) arg));
    break;

    case IO_GPIO_SET_INT_ENABLE:
    REG_WRITE(INTEN, *((uint32_t *) arg));
    break;

    case IO_GPIO_GET_INT_ENABLE:
    *((uint32_t *) arg) = REG_READ(INTEN);
    break;

    case IO_GPIO_SET_INT_MASK:
    REG_WRITE(INTMASK, *((uint32_t *) arg));
    break;

    case IO_GPIO_GET_INT_MASK:
    *((uint32_t *) arg) = REG_READ(INTMASK);
    break;

    case IO_GPIO_SET_INT_TYPE:
    REG_WRITE(INTTYPE_LEVEL, *((uint32_t *) arg));
    break;

    case IO_GPIO_SET_INT_POLARITY:
    REG_WRITE(INT_POLARITY, *((uint32_t *) arg));
    break;

    case IO_GPIO_SET_INT_BOTHEDGE:
    REG_WRITE(INT_BOTHEDGE, *((uint32_t *) arg));
    break;

    case IO_GPIO_SET_DEBOUNCE:
    if (dev->enableDebounce == 1) {
        REG_WRITE(DEBOUNCE, *((uint32_t *) arg));
    }
    break;

    case IO_GPIO_GET_DIRECTION:
    *((uint32_t *) arg) = REG_READ(SWPORTA_DDR);
    break;

    case IO_GPIO_GET_GPO:
    *((uint32_t *) arg) = REG_READ(SWPORTA_DR);
    break;

    default:
    break;
    }
}


static void gpio_ISR_proc(uint32_t dev_id)
{
    gpio_info_pt dev = gpio_handles[dev_id];
    uint32_t status = REG_READ(INTSTATUS);

    if (!status)
    return;         /* Spurious interrupts. */

    if (_Usually(NULL != dev->rx_cb))
    dev->rx_cb(status);

    REG_WRITE(PORTA_EOI, status);   /* clear status register. */
}


#endif
